//
//  BEObjcRuntime.swift
//  BasicExtensions
//
//  Created by Fanxx on 2018/3/23.
//  Copyright © 2018年 fanxx. All rights reserved.
//

import Foundation

public protocol BERuntimeExtension: AnyObject {
    
}
extension NSObject: BERuntimeExtension { }

// MARK: OC创建Swift类

extension NSObject {
    ///提供ObjC调用继承自NSObject的Swift类的方法，防止有些类使用了Swift的特性导致对ObjC类不可见
    ///params传的属性需在Swift中添加@objc标记
    public static func createObject<T: NSObject>(_ name: String, params: [String: Any]? = nil) -> T? {
        if let cls = NSClassFromString("\(Bundle.main.namespace).\(name)") as? T.Type {
            let obj = cls.init()
            if let ps = params {
                obj.setValuesForKeys(ps)
            }
            return obj
        }
        return nil
    }
}
extension NSObject {
    ///提供ObjC调用继承自NSObject的Swift类的方法，防止有些类使用了Swift的特性导致对ObjC类不可见
    @objc public class func createObject(_ name: String) -> Self? {
        return Self.createObject(name)
    }
    ///提供ObjC调用继承自NSObject的Swift类的方法，防止有些类使用了Swift的特性导致对ObjC类不可见
    @objc public class func createObject(_ name: String, params: [String: Any]) -> Self? {
        return Self.createObject(name, params: params)
    }
}

// MARK: 扩展属性

/// 管理关联对象
public struct BERuntimeAssociatedManager {
    let base: AnyObject
    public func object<T>(for key: String) -> T? {
        if let idx = UnsafeRawPointer(bitPattern: key.hashValue) {
            return objc_getAssociatedObject(self.base, idx) as? T
        }else{
            return nil
        }
    }
    public func setObject(retain object: Any?, for key: String) {
        if let idx = UnsafeRawPointer(bitPattern: key.hashValue) {
            objc_setAssociatedObject(self.base, idx, object, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    public func setObject(copy object: Any?, for key: String) {
        if let idx = UnsafeRawPointer(bitPattern: key.hashValue) {
            objc_setAssociatedObject(self.base, idx, object, .OBJC_ASSOCIATION_COPY_NONATOMIC)
        }
    }
    public func setObject(assign object: Any?, for key: String) {
        if let idx = UnsafeRawPointer(bitPattern: key.hashValue) {
            objc_setAssociatedObject(self.base, idx, object, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
    public subscript<T>(key: String) -> T? {
        get {
            return self.object(for: key)
        }
        nonmutating set {
            self.setObject(retain: newValue, for: key)
        }
    }
    public subscript<T>(copy key: String) -> T? {
        get {
            return self.object(for: key)
        }
        nonmutating set {
            self.setObject(copy: newValue, for: key)
        }
    }
    public subscript<T>(assign key: String) -> T? {
        get {
            return self.object(for: key)
        }
        nonmutating set {
            self.setObject(assign: newValue, for: key)
        }
    }
    /// 不存在则创建并返回，存在则直接返回，线程安全
    public func object<T>(for key: String, createIfNil creation: @autoclosure () -> T) -> T {
        if let obj: T = self.object(for: key) {
            return obj
        }else{
            objc_sync_enter(self.base)
            defer {
                objc_sync_exit(self.base)
            }
            if let obj: T = self.object(for: key) {
                return obj
            }else{
                let obj = creation()
                self.setObject(retain: obj, for: key)
                return obj
            }
        }
    }
}

extension BERuntimeExtension {
    /// 扩展属性
    /// 可通过self.extend["key"] = value, self.extend[assign: "key"] = value, self.extend[copy: "key"] = value，self.extend["key"]来设置获取扩展属性
    public var extend: BERuntimeAssociatedManager {
        return .init(base: self)
    }
}

// MARK: 直接调用方法

/// 通过selector来调用方法, 最多传11个参数
public func callMethod(for target: AnyObject, action selector: Selector, _ args: Any...) {
    callMethod(for: target, action: selector, args: args)
}
/// 通过selector来调用方法, 最多传11个参数
public func callMethod(for target: AnyObject, action selector: Selector, args: [Any]) {
    if let method = class_getInstanceMethod(type(of: target), selector) {
        let imp = method_getImplementation(method)
        switch args.count {
        case 0:
            typealias Function = @convention(c) (AnyObject, Selector) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector)
        case 1:
            typealias Function = @convention(c) (AnyObject, Selector, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0])
        case 2:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1])
        case 3:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2])
        case 4:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3])
        case 5:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4])
        case 6:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4], args[5])
        case 7:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4], args[5], args[6])
        case 8:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7])
        case 9:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
        case 10:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9])
        default:
            typealias Function = @convention(c) (AnyObject, Selector, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any) -> Void
            let function = unsafeBitCast(imp, to: Function.self)
            function(target, selector, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10])
        }
    }
}

extension BERuntimeExtension {
    /// 通过selector来调用方法, 最多传11个参数
    public func perform(_ selector: Selector, _ args: Any...) {
        callMethod(for: self, action: selector, args: args)
    }
}

// MARK: 黑魔法

extension BERuntimeExtension {
    ///黑魔法，切换实例方法
    @discardableResult
    public static func swizzle(_ originalSel: Selector, to newSel:Selector) -> Bool {
        guard let originalMethod = class_getInstanceMethod(self, originalSel),
              let newMethod = class_getInstanceMethod(self, newSel) else {
            return false
        }
        
        class_addMethod(self,
                        originalSel,
                        class_getMethodImplementation(self, originalSel)!,
                        method_getTypeEncoding(originalMethod))
        class_addMethod(self,
                        newSel,
                        class_getMethodImplementation(self, newSel)!,
                        method_getTypeEncoding(newMethod));
        
        method_exchangeImplementations(class_getInstanceMethod(self, originalSel)!,
                                       class_getInstanceMethod(self, newSel)!)
        
        return true
    }
    ///黑魔法，切换类方法
    @discardableResult
    public static func swizzle(class originalSel: Selector, to newSel:Selector) -> Bool {
        let cls: AnyClass? = object_getClass(self)
        guard let originalMethod = class_getInstanceMethod(cls, originalSel),
              let newMethod = class_getInstanceMethod(cls, newSel) else {
            return false
        }
        method_exchangeImplementations(originalMethod, newMethod)
        return true
    }
}

// MARK: MRC

extension BERuntimeExtension {
    /// 转换为Unmanaged对象，可操作引用计数
    public var asUnmanaged: Unmanaged<Self> {
        return .passUnretained(self)
    }
    ///获取对象内存地址
    public var memoryAddress: UnsafeMutableRawPointer {
        return self.asUnmanaged.toOpaque()
    }
    ///从内存地址获取对象
    public static func take(from memoryAddress: UnsafeRawPointer) -> Self {
        return Unmanaged<Self>.fromOpaque(memoryAddress).takeUnretainedValue()
    }
    ///添加引用计数，防止被释放
    @discardableResult
    public func retain() -> Unmanaged<Self>{
        return self.asUnmanaged.retain()
    }
    ///释放引用，需与retain配对，有多少次retain就要多少次release，否则会造成内存泄漏
    public func release() {
        self.asUnmanaged.release()
    }
}

/// 执行不安全的操作
public struct Unsafe<ObjectType: AnyObject> {
    public let unmanaged: Unmanaged<ObjectType>
    public init(_ object: ObjectType) {
        self.unmanaged = .passUnretained(object)
    }
    ///获取对象内存地址
    public var memoryAddress: UnsafeMutableRawPointer {
        return self.unmanaged.toOpaque()
    }
    ///从内存地址获取对象
    public static func take(from memoryAddress: UnsafeRawPointer) -> ObjectType {
        return Unmanaged<ObjectType>.fromOpaque(memoryAddress).takeUnretainedValue()
    }
    ///添加引用计数，防止被释放
    @discardableResult
    public func retain() -> Unmanaged<ObjectType>{
        return self.unmanaged.retain()
    }
    ///释放引用，需与retain配对，有多少次retain就要多少次release，否则会造成内存泄漏
    public func release() {
        self.unmanaged.release()
    }
}

/// 线程安全，仿oc的@synchronized
public func synchronized(_ lock: Any, closure: () -> Void) {
    objc_sync_enter(lock)
    closure()
    objc_sync_exit(lock)
}

/// 加载运行时钩子，在AppDelegate中程序启动时执行
/// 部分常用需要hock的方法添加了对应的属性可使用kvo来观察值变化来做相应和处理
public func loadRuntimeHook() {
    UIViewController.__beHook()
    UIView.__beHook()
}
